package com.wzcl.app.service.impl;

import com.alibaba.fastjson.JSON;
import com.wzcl.app.dao.model.*;
import com.wzcl.app.dao.repository.*;
import com.wzcl.app.model.common.*;
import com.wzcl.app.model.dto.CartDto;
import com.wzcl.app.service.CartService;
import com.wzcl.app.service.CommodityService;
import com.wzcl.app.service.SpecificationsService;
import com.wzcl.app.service.UserService;
import com.wzcl.app.utils.CollectionUtil;
import com.wzcl.app.utils.LockUtil;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.util.*;
import java.util.stream.Collectors;


@Log4j2
@Service
public class CartServiceImpl implements CartService {
    private static final String CREATE_KEY = "CART_CREATE_LOCK_";

    @Resource
    private CartRepository repository;
    @Resource
    private OrdersRepository ordersRepository;
    @Resource
    private CommodityService commodityService;
    @Resource
    private UserService userService;
    @Resource
    private SpecificationsService specificationsService;
    @Resource
    private CommodityRepository commodityRepository;
    @Resource
    private UserCourseRepository userCourseRepository;
    @Resource
    private CourseRepository courseRepository;


    @Override
    public Cart addCart(CartDto cartDto) throws BusinessException {
        //获取用户
        AppUser user = userService.getAndCheckByPhone(cartDto.getPhone());
        if (LockUtil.isLocked(CREATE_KEY + user.getId() + "_" + cartDto.getCommodityId())) {
            throw new BusinessException(CodeMsg.CART_CREATING);
        }
        try {
            LockUtil.lock(CREATE_KEY + user.getId() + "_" + cartDto.getCommodityId(), 5 * 60);
            //商品校验
            Commodity commodity = commodityService.getAndCheckCommodityById(cartDto.getCommodityId());
            String specStr = null;
            //开启了规格属性
            if(commodity.getSpec()){
                Specifications spec = specificationsService.getByCommodityIdAndSpec(commodity.getId(),cartDto.getSpec());
                if(spec == null){
                    throw new BusinessException(CodeMsg.SYSTEM_ERR,"规格选择错误");
                }
                specStr = spec.getSpec();
            }
            //重复校验
            RVO exist = isExist(user.getId(), cartDto.getCommodityId(),specStr);
            //不重复添加
            if (exist.getCode() == 500) {
                throw new BusinessException(CodeMsg.CART_NOT_REPEAT, "该商品已购买或已生成订单!");
            }
            //新增信息
            Cart cart = new Cart();
            cart.setUserId(user.getId());
            cart.setUserPhone(user.getPhone());
            cart.setCommodity(commodity);
            cart.setSpec(specStr);
            cart.setState(WZCL.STATE.NOT_DELETE);
            return repository.save(cart);
        } catch (BusinessException e) {
            throw e;
        } catch (Exception e) {
            e.printStackTrace();
            throw new BusinessException(CodeMsg.SYSTEM_ERR);
        } finally {
            LockUtil.unlock(CREATE_KEY + user.getId() + "_" + cartDto.getCommodityId());
        }


    }

    /**
     * 用户批量删除购物车
     *
     * @param cartDto 购物车
     * @return
     */
    @Override
    @Transactional
    public Cart delPhysicalCart(CartDto cartDto) {
        //校验
        Cart cart = getAndCheckById(cartDto);
        //校验用户是否匹配
        if (!StringUtils.equals(cartDto.getPhone(), cart.getUserPhone())) {
            throw new BusinessException(CodeMsg.USER_NOT_MATCH);
        }
        //删除
        log.info("Cart:{} Physical deletion", JSON.toJSONString(cart));
        repository.delete(cart);
        return cart;
    }

    /**
     * 创建订单,逻辑删除购物车
     */
    @Override
    public void delCartByOrders(List<Cart> carts, Orders orders) {
        log.info("Cart Id:{} logic deletion", StringUtils.join(carts.stream().map(cart -> cart.getId()).collect(Collectors.toList()), ","));
        carts.stream().forEach(cart -> {
            cart.setOrderId(orders.getId());
            cart.setState(WZCL.STATE.IS_DELETE);
        });
        repository.saveAll(carts);
    }

    /**
     * 订单完成,物理删除购物车
     */
    @Override
    public void delCartByOrders(Orders orders) {
        List<Cart> cartList = repository.findAllByOrderId(orders.getId());
        if (cartList != null && cartList.size() > 0) {
            log.info("Orders ID:{} has paid,Cart Id:{} Physical deletion", orders.getId(), StringUtils.join(cartList.stream().map(cart -> cart.getId()).collect(Collectors.toList()), ","));
            repository.deleteAll(cartList);
        }
    }

    /**
     * 取消订单,恢复购物车
     */
    @Override
    public void restoreCartByOrders(Orders orders) {
        List<Cart> cartList = repository.findAllByOrderId(orders.getId());
        if (cartList != null && cartList.size() > 0) {
            cartList.stream().forEach(cart -> {
                cart.setOrderId(null);
                cart.setState(WZCL.STATE.NOT_DELETE);
            });
            log.info("Cart Id:{} logic restore", StringUtils.join(cartList.stream().map(cart -> cart.getId()).collect(Collectors.toList()), ","));
            repository.saveAll(cartList);
        }
    }


    @Override
    public List<Cart> delPhysicalCartList(List<Cart> carts) {
        log.info("Cart:{} Physical deletion", JSON.toJSONString(carts));
        repository.deleteAll(carts);
        return carts;
    }

    @Override
    public Cart updateCart(CartDto cartDto) {
        //校验获取
        Cart cart = getAndCheckById(cartDto);
        //修改
        BeanUtils.copyProperties(cartDto, cart, "id", "state", "user");
        return repository.save(cart);
    }

    public Cart getCartById(Integer id) {
        Optional<Cart> optional = repository.findById(id);
        return optional.orElse(null);
    }

    @Override
    public Cart getAndCheckById(Integer id) {
        if (id == null) {
            throw new BusinessException(CodeMsg.MUST_BE_NOT_NULL, "缺少购物车Id");
        }
        Cart cart = getCartById(id);
        if (cart == null) {
            throw new BusinessException(CodeMsg.CART_NOT_EXIST);
        }
        return cart;
    }

    @Override
    public List<Cart> getCartById(List<Integer> ids) {
        return repository.findAllById(ids);
    }

    @Override
    public List<Cart> getCartList(CartDto cartDto) {
        //获取购物车商品
        List<Cart> all = repository.findAll(getSpecification(cartDto), cartDto.getSort());
        // 查询商品是否下架，如果下架则踢出购物车
        for (int index = 0; index < all.size(); index++) {
            Boolean saleById = commodityRepository.findSaleById(all.get(index).getCommodity().getId());
            if (saleById == false){
                all.remove(index);
                // 移除元素后 索引会整体向前移动 所以索引要重新减一
                index -- ;
            }
        }
        return all;
    }

    @Override
    public MyPage<Cart> getCartPage(CartDto cartDto) {
        List<Cart> all = repository.findAll(getSpecification(cartDto));
        ArrayList<Cart> list = new ArrayList<>(all);
        // 查询商品是否下架，如果下架则踢出购物车
        for (int index = 0; index < list.size(); index++) {
            Boolean saleById = commodityRepository.findSaleById(list.get(index).getCommodity().getId());
            if (saleById == false){
                list.remove(index);
                // 移除元素后 索引会整体向前移动 所以索引要重新减一
                index -- ;
            }else {
                //通过商品信息查找课程
                Course course = courseRepository.findAllByCourseId(list.get(index).getCommodity().getResourceId());
                list.get(index).setCourse(course);
            }
        }
        List<Cart> listBySort = CollectionUtil.getListBySort(list, "getCreateTime", "desc");
        List<Cart> listByPage = CollectionUtil.getListByPage(listBySort, cartDto.getPageNumber()+1, cartDto.getPageSize());
        MyPage<Cart> myPage = new MyPage<>();
        myPage.setContent(listByPage);
        myPage.setTotalElements(all.size());
        return myPage;
    }

    /**
     * 购物车是否存在（添加购物车时做校验）
     * @param userId
     * @param commodityId
     * @return
     */
    private synchronized RVO isExist(Integer userId, Integer commodityId, String spec) {
        String phone = SecurityContextHolder.getContext().getAuthentication().getName();
        //查询用户当前最近的未支付订单
        Orders order = ordersRepository.findAllNotPayByPayStateOfRecently(phone);
        // 添加购物车之前校验用户是否已购买该商品
        UserCourse userCourse = userCourseRepository.findAllByCourseIdAndUserPhone(commodityRepository.findResourceIdById(commodityId), phone);
        //确认用户是否是直接下单（直接下单只能购买一个商品）
        if (order != null){
            if (order.getDetails().size() == 1){
                // 校验用户是否重复下单
                Commodity commodity = order.getDetails().get(0).getCommodity();
                if (commodity.getId() == commodityId){
                    return RVO.error("订单已生成,请勿重复下单");
                }
            }
        }
        if (userCourse != null){
            return RVO.error("商品已添加或订单已生成");
        }
        //校验购物车
        Cart cart = null;
        if(StringUtils.isBlank(spec)){
            // 校验购物车中是否已存在该商品
            cart = repository.findByUserIdAndCommodityIdAndNullSpec(userId, commodityId, phone);
        }else {
            cart = repository.findByUserIdAndCommodityIdAndSpec(userId,commodityId,spec, phone);
        }
        if (cart != null) {
            if (WZCL.STATE.IS_DELETE.equals(cart.getState())) {
                throw new BusinessException(CodeMsg.CART_NOT_REPEAT, "该商品已购买或已生成订单!");
            }
            return RVO.error("商品已添加或订单已生成");
        }
        return RVO.success("校验通过");
    }

    private Specification<Cart> getSpecification(CartDto cartDto) {
        return new Specification<Cart>() {
            @Override
            public Predicate toPredicate(Root<Cart> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                List<Predicate> predicates = new ArrayList<Predicate>();
                if (StringUtils.isNotBlank(cartDto.getPhone())) {
                    predicates.add(criteriaBuilder.equal(root.get("userPhone"), cartDto.getPhone()));
                }
                predicates.add(criteriaBuilder.equal(root.get("state"), WZCL.STATE.NOT_DELETE));
                return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
            }
        };
    }
}
